#include <openssl/err.h>

#include "./SslConnection.h"
#include "../../../tcp/code/utility/Logger.h"

SslConnection::SslConnection(const TcpConnectionPtr &tcpConn, SslContext *sslCtx)
    : tcpConn_(tcpConn), sslCtx_(sslCtx)
{
    // SSL连接
    this->sslConn_ = ::SSL_new(this->sslCtx_->getContext());
    if (!this->sslConn_)
    {
        LOG_ERROR("Failed to create SSL object: %s", ::ERR_error_string(::ERR_get_error(), nullptr));
        return;
    }

    // BIO
    this->readBio_ = ::BIO_new(::BIO_s_mem()); // OpenSSL的方法，内存类型
    this->writeBio_ = ::BIO_new(::BIO_s_mem());
    if (!this->readBio_ || !this->writeBio_)
    {
        LOG_ERROR("Failed to create BIO objects");
        ::SSL_free(this->sslConn_); // 注意释放
        this->sslConn_ = nullptr;
        return;
    }

    ::SSL_set_bio(this->sslConn_, this->readBio_, this->writeBio_);
    ::SSL_set_accept_state(this->sslConn_); // 设置为服务器模式

    // SSL选项
    // 允许在调用 SSL_write 时，传入的缓冲区地址在调用之间发生变化
    // 默认情况下，OpenSSL 假设同一块内存会被重复用于写入操作
    // 设置这个选项后，即使每次写入时传入不同的缓冲区地址也不会有问题
    SSL_set_mode(this->sslConn_, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
    // 允许部分写入操作
    // 当设置此选项时，SSL_write 可能只写入部分数据而不是全部
    // 这在处理大块数据或网络拥塞时很有用，允许逐步发送数据
    SSL_set_mode(this->sslConn_, SSL_MODE_ENABLE_PARTIAL_WRITE);

    // 读回调
    this->tcpConn_->setReadWriteCallback(
        std::bind(&SslConnection::onRead, this, std::placeholders::_1,
                  std::placeholders::_2, std::placeholders::_3));
}

SslConnection::~SslConnection()
{
    if (this->sslConn_)
    {
        ::SSL_free(this->sslConn_); // 会同时释放BIO
    }
}

void SslConnection::onRead(const TcpConnectionPtr &conn, Buffer *buf,
                           Timestamp time)
{
    if (this->sslState_ == SslState::Handshake)
    {
        // 将数据写入BIO
        ::BIO_write(this->readBio_, buf->readableBegin(), buf->getReadableSize());
        buf->readableIndexReset(buf->getReadableSize());

        this->handleHandshake();
        return;
    }
    else if (this->sslState_ == SslState::Established)
    {
        // 解密数据
        char decryptedData[4096]{};
        int ret = ::SSL_read(this->sslConn_, decryptedData, sizeof(decryptedData));

        if (ret > 0)
        {
            // 创建新的Buffer存储解密后的数据
            Buffer decryptedBuffer{};
            decryptedBuffer.appendWrite(decryptedData, ret);

            // 调用上层回调处理解密后的数据
            if (this->messageCallback_)
            {
                this->messageCallback_(conn, &decryptedBuffer, time);
            }
        }
    }
}

void SslConnection::handleHandshake()
{
    int ret = ::SSL_do_handshake(this->sslConn_);
    if (ret == 1)
    {
        this->sslState_ = SslState::Established;
        LOG_INFO("SSL handshake completed successfully");
        LOG_INFO("Using cipher: %s", SSL_get_cipher(this->sslConn_));
        LOG_INFO("Protocol version: %s", ::SSL_get_version(this->sslConn_));

        // 握手完成后，确保设置了正确的回调
        // 需要上层设置回调处理后一个状态解密的数据
        if (!this->messageCallback_)
        {
            LOG_ERROR("No message callback set after SSL handshake");
        }
        return;
    }

    int err = ::SSL_get_error(this->sslConn_, ret);
    LOG_INFO("SSL_get_error returned: %d", err);
    switch (err)
    {
    case SSL_ERROR_WANT_READ:
        LOG_INFO("SSL handshake needs more data to read - waiting for more data");
        break; // 正常的握手过程，需要继续
    case SSL_ERROR_WANT_WRITE:
        LOG_INFO("SSL handshake needs more data to write - waiting for more data");
        break;
    default:
    {
        // 获取详细的错误信息
        char errBuf[256]{};
        unsigned long errCode = ::ERR_get_error();
        ::ERR_error_string_n(errCode, errBuf, sizeof(errBuf));
        LOG_ERROR("SSL handshake failed: %s", errBuf);

        this->tcpConn_->shutdown(); // 关闭连接
        break;
    }
    }

    // 调试时会到达SSL_ERROR_WANT_READ卡在server hello未发出去，需要发送
    char buf[4096]{};
    int needWriteSize{};
    while ((needWriteSize = BIO_pending(this->writeBio_)) > 0) // 有等待发送则循环发送
    {
        // 从BIO读出，向外写入
        int readSize = ::BIO_read(this->writeBio_, buf,
                                  std::min(needWriteSize, static_cast<int>(sizeof(buf))));
        if (readSize > 0)
        {
            // this->writeBuffer_.appendWrite(buf, len);
            // this->tcpConn_->sendData(&this->writeBuffer_);
            this->tcpConn_->sendData(buf, readSize);
        }
    }
}

void SslConnection::startHandshake()
{
    ::SSL_set_accept_state(this->sslConn_);
    this->handleHandshake();
}

void SslConnection::onEncrypted(const char *data, size_t len)
{
    this->writeBuffer_.appendWrite(data, len);
    this->tcpConn_->sendData(&this->writeBuffer_);
}

void SslConnection::onDecrypted(const char *data, size_t len)
{
    this->decryptedBuffer_.appendWrite(data, len);
}

SslError SslConnection::getLastError(int ret)
{
    int error = ::SSL_get_error(this->sslConn_, ret);
    switch (error)
    {
    case SSL_ERROR_NONE:
        return SslError::None;
    case SSL_ERROR_WANT_READ:
        return SslError::WantRead;
    case SSL_ERROR_WANT_WRITE:
        return SslError::WantWrite;
    case SSL_ERROR_SYSCALL:
        return SslError::SysCall;
    case SSL_ERROR_SSL:
        return SslError::Ssl;
    default:
        return SslError::Unknown;
    }
}

void SslConnection::handleError(SslError error)
{
    switch (error)
    {
    case SslError::WantRead:
    case SslError::WantWrite:
        break; // 需要等待更多数据或写入缓冲区可用
    case SslError::SysCall:
    case SslError::Ssl:
    case SslError::Unknown:
        LOG_ERROR("SSL error occurred: %s", ::ERR_error_string(::ERR_get_error(), nullptr));
        this->sslState_ = SslState::Error;
        this->tcpConn_->shutdown(); // 注意关闭
        break;
    default:
        break;
    }
}

void SslConnection::send(const void *data, size_t len)
{
    if (this->sslState_ != SslState::Established)
    {
        LOG_ERROR("Cannot send data before SSL handshake is complete");
        return;
    }

    int writtenSize = ::SSL_write(this->sslConn_, data, len);
    if (writtenSize <= 0)
    {
        int error = ::SSL_get_error(this->sslConn_, writtenSize);
        LOG_ERROR("SSL_write failed: %s", ::ERR_error_string(error, nullptr));
        return;
    }

    char buf[4096]{};
    int needWriteSize{};
    while ((needWriteSize = BIO_pending(this->writeBio_)) > 0) // 有等待发送则循环发送
    {
        // 从BIO读出，向外写入
        int readSize = ::BIO_read(this->writeBio_, buf,
                                  std::min(needWriteSize, static_cast<int>(sizeof(buf))));
        if (readSize > 0)
        {
            // this->writeBuffer_.appendWrite(buf, len);
            // this->tcpConn_->sendData(&this->writeBuffer_);
            this->tcpConn_->sendData(buf, readSize);
        }
    }
}

BIO_METHOD *createCustomBioMethod()
{
    BIO_METHOD *method = ::BIO_meth_new(BIO_TYPE_MEM, "custom");
    ::BIO_meth_set_read(method, SslConnection::bioRead);
    ::BIO_meth_set_write(method, SslConnection::bioWrite);
    ::BIO_meth_set_ctrl(method, SslConnection::bioCtrl);
    return method;
}

int SslConnection::bioRead(BIO *bio, char *data, int len)
{
    SslConnection *sslConn = static_cast<SslConnection *>(::BIO_get_data(bio)); // 从BIO获取连接
    if (!sslConn)
        return -1;

    size_t readableSize = sslConn->readBuffer_.getReadableSize();
    if (readableSize == 0) // 无数据可读
    {
        return -1;
    }

    size_t needReadSize = std::min(static_cast<size_t>(len), readableSize); // 最小可读大小
    memcpy(data, sslConn->readBuffer_.readableBegin(), needReadSize);
    sslConn->readBuffer_.readableIndexReset(needReadSize);
    return needReadSize;
}

int SslConnection::bioWrite(BIO *bio, const char *data, int len)
{
    SslConnection *sslConn = static_cast<SslConnection *>(::BIO_get_data(bio));
    if (!sslConn)
        return -1;

    // sslConn->writeBuffer_.appendWrite(data, len);
    // sslConn->tcpConn_->sendData(&sslConn->writeBuffer_);
    sslConn->tcpConn_->sendData(data, len); // 直接写到tcpConn，未用writeBuffer_
    return len;
}

long SslConnection::bioCtrl(BIO *bio, int cmd, long num, void *ptr)
{
    switch (cmd)
    {
    case BIO_CTRL_FLUSH: // 暂时未实现刷新操作
        return 1;
    default:
        return 0;
    }
}